#include "StdAfx.h"
#include "PhysicsGeometry.h"
#include "Utility\ColladaGeometry.h"

static const u32 s_uHash_Box = 0xb87da6e;
static const u32 s_uHash_Sphere = 0x1;


PhysicsGeometry::PhysicsGeometry()
{
	m_pBoundingVolumes = 0;	
}


PhysicsGeometry::~PhysicsGeometry(void)
{
	if( m_pBoundingVolumes )
		m_pBoundingVolumes->removeReference();
}

/**
 * Create the smallest sphere that encloses all of the vertices in the geometry
 **/
hkpConvexTranslateShape* PhysicsGeometry::CreateSphere(ColladaGeometry* geometry, Vector4& vAbsMin, Vector4& vAbsMax)
{
	Vector4 vMin, vMax;
	FindMinMax(geometry->GetPositions(), vMin, vMax);

	Vector4 vCenter = vMin + ( vMax - vMin ) * 0.5f;	
	float fRadius = (vCenter - vMin).Length();
	hkpSphereShape* sphere = new hkpSphereShape(fRadius);
	hkVector4 translate(vCenter.GetX(), vCenter.GetY(), vCenter.GetZ(), vCenter.GetW());

	hkpConvexTranslateShape* pRet = new hkpConvexTranslateShape(sphere, translate);
	sphere->removeReference();  //remove reference since returned shape now owns
	
	return pRet;

	
}

/**
 * Create the smallest box that encloses all of the vertices in the geometry
 **/
hkpConvexTranslateShape* PhysicsGeometry::CreateBox(ColladaGeometry* geometry, Vector4& vAbsMin, Vector4& vAbsMax)
{
	Vector4 vMin, vMax;
	FindMinMax(geometry->GetPositions(), vMin, vMax);

	hkVector4 halfExtends( (vMax.GetX() - vMin.GetX()) * 0.5f, (vMax.GetY() - vMin.GetY()) * 0.5f, (vMax.GetZ() - vMin.GetZ()) * 0.5f, 1.0f);
	hkpBoxShape* box = new hkpBoxShape(halfExtends);

	Vector4 vCenter = vMin + ( vMax - vMin ) * 0.5f;	
	hkVector4 translate(vCenter.GetX(), vCenter.GetY(), vCenter.GetZ(), vCenter.GetW());

	hkpConvexTranslateShape* pRet = new hkpConvexTranslateShape(box, translate);
	box->removeReference(); //remove refernce since returned shape owns this box
	return pRet;
}

void PhysicsGeometry::FindMinMax(FixedArray<Vector4>* verts, Vector4& vMin, Vector4& vMax)
{
	for(int i = 0; i < verts->GetNumElements(); i++)
	{
		Vector4 v = (*verts)[i];

		if( v.GetX() < vMin.GetX() )
			vMin.SetX(v.GetX() );

		if( v.GetY() < vMin.GetY() )
			vMin.SetY(v.GetY() );

		if( v.GetZ() < vMin.GetZ() )
			vMin.SetZ(v.GetZ() );

		if( v.GetX() > vMax.GetX() )
			vMax.SetX(v.GetX() );

		if( v.GetY() > vMax.GetY() )
			vMax.SetY(v.GetY() );

		if( v.GetZ() > vMax.GetZ() )
			vMax.SetZ(v.GetZ() );
	}
}

void PhysicsGeometry::Load(const char* szFile)
{
	bool bRet = false;

	ColladaFile* file = new ColladaFile();
	bRet = file->Load(szFile);

	if( bRet )
	{
		Init(file->GetAllGeometry());	
	}
	

	delete file;
}

void PhysicsGeometry::Init(FixedArray<ColladaGeometry*>* geometries)
{
	hkArray<hkpShape*> shapeArray(0);	
	Vector4 vAbsMax;
	Vector4 vAbsMin;

	/**
	 * Go through the geometry that has been loaded by collada. The default naming convention for 
	 * collision geometry is shape_identifier.
	 **/
	for(int i = 0; i < geometries->GetNumElements(); i++)
	{
		ColladaGeometry* geometry = (*geometries)[i];
		
		const char* szName = geometry->GetName();
		char* identifier = strchr((char*)szName, '_');

		u32 type = StringHash(szName);
		u32 id = 0;
		
		if( identifier ) { StringHash(identifier); }

		hkpShape* shape = NULL;

		switch(type)
		{
		case s_uHash_Box:
			shape = CreateBox(geometry, vAbsMin, vAbsMax);
			break;
		case s_uHash_Sphere:
			shape = CreateSphere(geometry, vAbsMin, vAbsMax);
			break;
		}

		if( shape )
			shapeArray.pushBack(shape);			
	}

	//create a global bounding sphere around the object
	Vector4 vCenter = vAbsMin + ( vAbsMax - vAbsMin ) * 0.5f;	
	float fRadius = (vCenter - vAbsMin).Length();
	hkpSphereShape* sphere = new hkpSphereShape(fRadius);
	hkVector4 translate(vCenter.GetX(), vCenter.GetY(), vCenter.GetZ(), vCenter.GetW());
	hkpConvexTranslateShape* pFinalSphere = new hkpConvexTranslateShape(sphere, translate);

	hkpListShape* shapeList = new hkpListShape(&shapeArray[0], shapeArray.getSize());
	m_pBoundingVolumes = new hkpBvShape(pFinalSphere, shapeList);

	
	shapeList->removeReference();
	sphere->removeReference();
	pFinalSphere->removeReference();	
}